﻿module PrintUtils 
open System
open StateMonad
open Environment

let (-->) : ParseState<Unit> =
  fun e ->
    (),
    { 
      e with NumTabs = e.NumTabs + 1
    }

let (<--) : ParseState<Unit> =
  fun e ->
    (), { e with NumTabs = max 0 (e.NumTabs - 1) }

let printfn s : ParseState<Unit> =
  fun e ->
    let mutable tabs = ""
    for i = 1 to e.NumTabs do
      do tabs <- tabs + "  "
    do e.print_string_nl (tabs + s)
    (), e

let add_const str : ParseState<string> =
  state{
    let! state = getState
    match state.Consts |> Seq.tryFindIndex((=) str) with
    | Some index -> return (sprintf "str%d" index)
    | None -> 
      state.Consts.Add(str)
      return (sprintf "str%d" (state.Consts.Count - 1))
  }

//let is_method_void is_method_void : ParseState<Unit> =
//  fun e ->
//    (), { e with IsMethodVoid = is_method_void }

let get_t_mem (t : EmitType.T_symbol) = 
  state{
    let! e = getState
    let offset =
      if t.IsArg then 8 + (t.Id * 4)
      else - 4 - (t.Id * 4)
    let sign =  if offset > 0 then "+"
                else "" 
    if offset <> 0 then
      return Mem(Mem.Mem ("[ %ebp " + sign + (string offset) + " ]"))
    else
      return Mem(Mem.Mem ("[ %ebp " + " ]"))
  }

let get_t_DWORD_address (t : EmitType.T_symbol) =
  state{
    let! e = getState
    if t.Id < 0 then
      return Mem (Mem.Mem "0")
    else
      let offset =
        if t.IsArg then 8 + (t.Id * 4)
        else -4 - (t.Id * 4)
      return DWordPtrReg(ebp,offset) 
  }

let get_t_this : State<MemReg> =
  state{
    let offset = 8
    return PtrReg(ebp,offset) 
  }



let get_t_address (t : EmitType.T_symbol) =
  state{
    let! e = getState
    if t.Id < 0 then
      return Mem (Mem.Mem "0")
    else
      let offset =
        if t.IsArg then 8 + (t.Id * 4)
        else -4-(t.Id * 4)
      return PtrReg(ebp,offset) 
  }

let get_ret_address : State<MemReg> =
  state{
    return PtrReg(ebp, 4)
  }

//let get_length_address (t : EmitType.T_symbol) =
//  state{
//    let! PtrReg(ebp,offset)  = get_t_address t
//    return PtrReg(ebp,offset) 
//  }
//let getReg
//let getConst 
//let get_t (t : EmitType.T_symbol) : ParseState<int> =
  



let cmp (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("cmp " + op1.GetValue + ", " + op2.GetValue)
  }

let print_label label =
  state{
    do! (<--)
    do! (<--)
    let label = label + ":"
    do! printfn label
    do! (-->)
    do! (-->)
  }

let jmp label =
  state{
    do! printfn ("jmp " + label)
  }

let jne label =
  state{
    do! printfn ("jne " + label)
  }

let je label =
  state{
    do! printfn ("je " + label)
  }

let jea label =
  state{
    do! printfn ("jea " + label)
  }

let jl label =
  state{
    do! printfn ("jl " + label)
  }

let push (op : MemRegConst) =
  state{
    do! printfn ("push " + op.GetValue)
  }

let pop (op : MemReg) =
  state{
    do! printfn ("pop " + op.GetValue)
  }

let lea (op1 : Reg) (op2 : Mem) =
  state{
    do! printfn ("lea " + op1.GetValue + ", " + op2.GetValue)
  }

let mov (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("mov " + op1.GetValue + ", " + op2.GetValue)
  }

let movzx (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("movzx " + op1.GetValue + ", " + op2.GetValue)
  }


let add (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("add " + op1.GetValue + ", " + op2.GetValue)
  }

let sub (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("sub " + op1.GetValue + ", " + op2.GetValue)
  }

let idiv (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("idiv " + op1.GetValue + ", " + op2.GetValue)
  }

let imul (op1 : Reg) (op2 : MemRegConst) =
  state{
    do! printfn ("imul " + op1.GetValue + ", " + op2.GetValue)
  }

let cdq =
  state{
    do! printfn "cdq "
  }

let sbb (op1 : Reg) (op2 : MemRegConst) = 
  state{
    do! printfn ("sbb " + op1.GetValue + ", " + op2.GetValue)
  }

let _and (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("and " + op1.GetValue + ", " + op2.GetValue)
  }

let _or (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("or " + op1.GetValue + ", " + op2.GetValue)
  }

let _not (op1 : MemReg) =
  state{
    do! printfn ("not " + op1.GetValue)
  }

let _xor (op1 : MemReg) (op2 : MemRegConst) =
  state{
    do! printfn ("xor " + op1.GetValue + ", " + op2.GetValue)
  }

let inc (op1 : MemReg) =
  state{
    do! printfn ("inc " + op1.GetValue)
  }

let dec (op1 : MemReg) =
  state{
    do! printfn ("dec " + op1.GetValue)
  }

let neg (op1 : Reg) =
  state{
    do! printfn ("neg " + op1.GetValue)
  }

let call label =
  state{
    do! printfn ("call " + label)
  }

let leave =
  state{
    do! printfn "leave"
  }

let ret =
  state{
    do! printfn "ret"
  }

let comment (str : string) = 
  state{
    let str = str.Replace("\n", "")
    do! printfn ("#" + str)
  }

//let sum_registers_with_mult_offset (reg1 : Reg) reg2 offset =
//  state{
//    return Ptr(reg1.PtrSumWithOffset reg2 offset)
//  }

let sum_registers_with_offset (reg1 : Reg) offset = 
  state{
    return PtrReg (reg1, offset)
  }

let set_zero (op1 : MemReg) =
  state{
    do! printfn ("xor " + op1.GetValue + ", " + op1.GetValue)
  }

let set comp =
  state{
    do! printfn (sprintf "set%s cl" comp)
  }

let test (arg1 : MemReg) (arg2 : MemRegConst) = 
  state{
    do! printfn (sprintf "test %s, %s" arg1.GetValue arg2.GetValue)
  }